package de.homelabs.bookmarkmanager.desktop.qt.signal;

import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.Method;

import javolution.util.FastList;

import org.apache.log4j.Logger;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Service;

import spiffy.log4j.LoggerHelper;

import com.trolltech.qt.QSignalEmitter.AbstractSignal;

import de.homelabs.bookmarkmanager.desktop.qt.stereotype.Signal;

@Service("annotationSignalHandler")
@Scope("singleton")
public class AnnotationSignalHandler {

	//logger
	 public static final Logger logger = LoggerHelper.getLogger();
	 
	//vars
	private FastList<Object> signalHandlers = new FastList<Object>();
	
	/**
	 * scans all registered beans for method annotation @Signal
	 * 
	 * @param context - Application Context
	 * @return int - Count registered Signal Handlers
	 */
	public int registerSignalHandler(ApplicationContext context){
		//counter for registered Handler
		int registeredHandlers = 0;
		
		//get all registerd Beans
		String[] beans = context.getBeanDefinitionNames();
		
		//lets search :-)
		for (String bean : beans){
			Object currentBean = context.getBean(bean);						//bean
			Method[] methods = currentBean.getClass().getMethods();			//bean methods
			for(Method method : methods){									//loop methods
				Annotation[] annotations = method.getAnnotations();			//get Method annotations
				for(Annotation annotation : annotations){
					if (annotation.annotationType().equals(Signal.class)){	//test Signal.class (@Signal)
						
							
						try {
							Signal signal = (Signal) annotation;				//annotation found, convert to Signal class
							
							//signatur of handler method
							StringBuffer handlerSignatur = new StringBuffer();	
							
							//handler method params
							StringBuffer handlerParams = extractHandlerMethodParameter(method);
							
							//prepare signatur to register
							handlerSignatur.append(method.getName());
							handlerSignatur.append("(");
							handlerSignatur.append(handlerParams);
							handlerSignatur.append(")");
							
							//get class that contains the signal, @Signal.clazz
							Object clazz =  (Object) context.getBean(signal.clazz());
							//get the signal @Signal.signal
							Field aSigasField = clazz.getClass().getField(signal.signal());
							//get concrete Signal Class
							AbstractSignal aSig = (AbstractSignal) aSigasField.get(clazz);
							
							//try to register
							logger.debug("SignalHandler to register: class="+signal.clazz()+", signal="+signal.signal()+", receiver="+currentBean.getClass().getSimpleName()+", handler method="+handlerSignatur);
							
							//register the annotated class as SignalHandler 
							//com.trolltech.qt.QSignalEmitter.AbstractSignal.connect(java.lang.Object receiver, java.lang.String method) 
							aSig.connect(currentBean, handlerSignatur.toString());
							
							//add handler bean to list
							this.signalHandlers.add((Object) currentBean);
							registeredHandlers++;
							logger.debug("SignalHandler registered: "+currentBean.getClass().getSimpleName());
							
						} catch (Exception e){
							logger.error("Fehler beim regstieren des Handler: receiver="+currentBean.toString());
							logger.error(e.getMessage());
						}
					}
				}
			}
		}
		return registeredHandlers;
	}
	
	/**
	 * return list of registered signal handlers
	 * @return FastList<Class> - SignalHandlers
	 */
	public FastList<Object> getRegisteredSignalHandlers(){
		return this.signalHandlers;
	}
	
	/**
	 * searches for a registers signal handler by name
	 * 
	 * @param beanName - handler name
	 * @return boolean
	 */
	public boolean isSignalHandlerRegistered(String beanName){
		//search in registerd Handlers
		for (FastList.Node<Object> clazz = signalHandlers.head(), end = signalHandlers.tail(); (clazz = clazz.getNext()) != end;) {
			if (clazz.toString().equalsIgnoreCase(beanName)){
				//Handler found
				return true;
			}
		} 
		
		//else no handler found
		return false;
	}
	
	/**
	 * extracts paramaters from method signature 
	 * 
	 * @param Method m - method to parse
	 * @return StringBuffer - parameters
	 */
	@SuppressWarnings("rawtypes")
	private StringBuffer extractHandlerMethodParameter(Method m){
		//tmp vars
		StringBuffer handlerParams = new StringBuffer();			//method params
		
		//loop over method params
		for (Class paramClass : m.getParameterTypes()){
			if (handlerParams.length() < 1)
				handlerParams.append(paramClass.getSimpleName());
			else
				handlerParams.append(","+paramClass.getSimpleName());
		}
		
		return handlerParams;
	}
}
